#include "CXMLSaxParser.h"
#include <lampe/StringUtils.h>
namespace lampe{
namespace xml{

String CXMLSaxParser::nodeTypeToString(int type){
	switch (type){
	case ELEMENT_START:
		return "ELEMENT_START";
	case ELEMENT_END:
		return "ELEMENT_END";
	case ELEMENT_TEXT:
		return "ELEMENT_TEXT";
	}
	return "";
}

void CXMLSaxParser::nodeReaded(){
/*
	if (getNodeType()==ELEMENT_START || getNodeType()==ELEMENT_END){
	printf("(%s) \"%s\"",nodeTypeToString(getNodeType()).c_str(),getNodeName().c_str());

	if (getNodeType()==ELEMENT_START){
		printf(" attribytes: ");
		for (u32 i=0;i<attribytes.size();i++)
			printf("\"%s\"=\"%s\" ",attribytes[i].name.c_str(),attribytes[i].value.c_str());
	}

	if (isEmptyElement())
		printf(" empty\n");
	else
		printf("\n");
	}
	*/
	if (getNodeType()==ELEMENT_TEXT){
		empty_node=true;
		//printf("TEXT=\"%s\"\n",text.c_str());
	}

	TagReaded();
}

bool CXMLSaxParser::isCDATA(){
	return cdata;
}

String CXMLSaxParser::getAttributeName(u32 index){
	return attribytes[index].name;
}

String CXMLSaxParser::getAttributeValue(u32 index){
	return attribytes[index].value;
}

void CXMLSaxParser::readComment(stream::IReader*reader){
	while (!reader->eof()){
		last_char=reader->readC8();
		if (last_char=='-'){
			last_char=reader->readC8();
			if (last_char=='-'){
				last_char=reader->readC8();
				break;
			}
		}
	}
}

void CXMLSaxParser::readCDATA(stream::IReader*reader){
		text="";
		empty_node=true;
		char old='[';
		int t=0;
		cdata=true;

		while (!reader->eof()){
			last_char=reader->readC8();

			if (last_char=='>' && t==1)
					break;

			if (last_char==']' && old==']')
				t++;
			else
				t=0;

			old=last_char;
			text+=last_char;
		}
		text=text.substr(0,text.size()-2);
		nodeReaded();
		text="";
		cdata=false;
	}

void CXMLSaxParser::readSpec(stream::IReader*reader){
		last_char=reader->readC8();
		if (last_char=='-'){
			last_char=reader->readC8();
			if (last_char=='-')
				readComment(reader);
			//else ERROR!!!
		}
		if (last_char=='['){
			last_char=reader->readC8();
			if (last_char=='C'){
				last_char=reader->readC8();
				if (last_char=='D'){
					last_char=reader->readC8();
					if (last_char=='A'){
						last_char=reader->readC8();
						if (last_char=='T'){
							last_char=reader->readC8();
							if (last_char=='A'){
								last_char=reader->readC8();
								if (last_char=='['){
									readCDATA(reader);
								}
							}
						}
					}
				}
			}

		}
	}

void CXMLSaxParser::setNodeType(int type){
		if (current_element==type)
			return;

		if (type==ELEMENT_TEXT)
			text="";

		if (current_element==ELEMENT_TEXT){
			attribytes.clear();
			tag_name="";

			text=str::trim(text);
			if (text.size()>0)
				nodeReaded();
		}

		if (current_element!=ELEMENT_TEXT){
			//printf("TEXT=\"%s\"\n",text.c_str());
		//else
			text="";
		}

		current_element=type;
	}

void CXMLSaxParser::setAtt(String name,String value){
		for (u32 i=0;i<attribytes.size();i++){
			if (attribytes[i].name==name){
				attribytes[i].value=value;
				return;
			}
		}
		att a;
		a.name=name;
		a.value=value;

		attribytes.push_back(a);
	}

	void CXMLSaxParser::setAtt(String name){
		setAtt(name,"");
	}


	void CXMLSaxParser::readSpace(stream::IReader*reader){
		while ((last_char==' ' || last_char=='\n' || last_char=='\t' || last_char=='\r') && !reader->eof()){
			last_char=reader->readC8();
		}
	}

	void CXMLSaxParser::readTagName(stream::IReader*reader){
		if (last_char==' ' || last_char=='/' || last_char=='>')
			return;
		String str="";
		do{
			str+=last_char;
			last_char=reader->readC8();
		}while (!reader->eof() && last_char!=' ' && last_char!='/' && last_char!='>');
		tag_name=str;
		//printf("tag name=\"%s\"\n",tag_name.c_str());
	}

	String CXMLSaxParser::readAttributeValue(stream::IReader*reader){
		if (last_char==' ' || last_char=='/' || last_char=='>')
			return "";
		bool q=last_char=='"';
		String str="";
		if (q)
			last_char=reader->readC8();
		do{
			str+=last_char;
			last_char=reader->readC8();
			if (!q && last_char==' ')
				break;
			if (q && last_char=='"'){
				last_char=reader->readC8();
				break;
			}
		}while (!reader->eof() && last_char!='>');
		//printf("att value=\"%s\"\n",str.c_str());
		return str;
	}

	String CXMLSaxParser::readAttributeName(stream::IReader*reader){
		if (last_char==' ' || last_char=='/' || last_char=='>')
			return "";
		String str="";
		do{
			str+=last_char;
			last_char=reader->readC8();
		}while (!reader->eof() && last_char!=' ' && last_char!='=' && last_char!='/'&& last_char!='>');
		//tag_name=str;
		//printf("att name=\"%s\"\n",tag_name.c_str());

		if (last_char=='='){
			last_char=reader->readC8();
			readSpace(reader);
			setAtt(str,readAttributeValue(reader));
		}else setAtt(str);
		return str;
	}

	void CXMLSaxParser::TagReaded(){
		if (process)
			process(this);
	}

CXMLSaxParser::CXMLSaxParser(){
	tag_name="";
	last_char=(char)0;
	empty_node=false;
	current_element=0;
	text="";
	cdata=false;
	tagObject=0;
}

int CXMLSaxParser::getNodeType(){
	return current_element;
}

String CXMLSaxParser::getNodeName(){
	return tag_name;
}

bool CXMLSaxParser::isEmptyElement(){
	return empty_node;
}

u32 CXMLSaxParser::getAttributeCount(){
	return attribytes.size();
}

String CXMLSaxParser::getNodeData(){
	return text;
}

void CXMLSaxParser::read(stream::IReader*reader,XmlSaxEvent p){
	process=p;
	reader->grab();
	reader->readLine();
	while (!reader->eof()){
		last_char=reader->readC8();
		if (last_char=='<'){
			last_char=reader->readC8();
			readSpace(reader);
			if (last_char=='!'){
				readSpec(reader);
			}else{
				setNodeType(ELEMENT_START);
				if (last_char=='/'){
					setNodeType(ELEMENT_END);
					last_char=reader->readC8();
					readSpace(reader);
				}
				readTagName(reader);
				while (last_char!='>' && last_char!='/' && !reader->eof()){
					readSpace(reader);
					readAttributeName(reader);
				}
				empty_node=last_char=='/';
				if (tag_name!="") nodeReaded();
			}
		}else{
			setNodeType(ELEMENT_TEXT);
			text+=last_char;
		}
	}
	reader->drop();
}

void CXMLSaxParser::setTag(void*t){
	tagObject=t;
}
void*CXMLSaxParser::getTag(){
	return tagObject;
}

}//namespace xml
}//namespace lampe
